#include "IntegerZ.h"
#include <assert.h>

//////////////////////////////////////////////////////////////////////
//
// Constructors
//
//////////////////////////////////////////////////////////////////////

Integer::Integer() {

  mpz_init(value);

}

Integer::Integer(int newval) {

  mpz_init_set_si(value, (signed long int) newval);

}

Integer::Integer(char * valstring) {

  mpz_init_set_str(value,valstring,10);

}

Integer::Integer(mpz_t newval) {

  mpz_init_set(value, newval);

}

//////////////////////////////////////////////////////////////////////
//
// Copy Constructor
//
//////////////////////////////////////////////////////////////////////

Integer::Integer(const Integer & ARG) {

  mpz_init_set(value, ARG.value);

}

//////////////////////////////////////////////////////////////////////
//
// Assignement
//
//////////////////////////////////////////////////////////////////////

Integer & Integer::operator = (const Integer & ARG) {

  mpz_set(value, ARG.value);

  return(*this);

}

Integer & Integer::operator = (int ARG) {

  if (ARG < 0) {

    mpz_set_ui(value, (unsigned long int) (- (long int) ARG));
    mpz_neg(value,value);

  } else {

    mpz_set_ui(value, (unsigned long int) ARG);

  }

  return(*this);

}

//////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////

Integer::~Integer() {

  mpz_clear(value);

}

//////////////////////////////////////////////////////////////////////
//
// Accessor
//
//////////////////////////////////////////////////////////////////////

char * Integer::Value() {

  return(mpz_get_str(NULL, 10, value));

}

//////////////////////////////////////////////////////////////////////
//
// Increment in-place (+=)
//
//////////////////////////////////////////////////////////////////////

Integer & Integer::operator += (int ARG) {

  if (ARG < 0) {

    mpz_sub_ui(value, value, (unsigned long int) (- (long int) ARG));

  } else {

    mpz_add_ui(value, value, (unsigned long int) ARG);

  }

  return(*this);

}

Integer & Integer::operator += (mpz_t ARG) {

  mpz_add(value, value, ARG);

  return(*this);

}

Integer & Integer::operator += (const Integer & ARG) {

  mpz_add(value, value, ARG.value);

  return(*this);

}

//////////////////////////////////////////////////////////////////////
//
// Decrement in-place (-=)
//
//////////////////////////////////////////////////////////////////////

Integer & Integer::operator -= (int ARG) {

  if (ARG < 0) {

    mpz_add_ui(value, value, (unsigned long int) (- (long int) ARG));

  } else {

    mpz_sub_ui(value, value, (unsigned long int) ARG);

  }

  return(*this);

}

Integer & Integer::operator -= (mpz_t ARG) {

  mpz_sub(value, value, ARG);

  return(*this);

}

Integer & Integer::operator -= (const Integer & ARG) {

  mpz_sub(value, value, ARG.value);

  return(*this);

}

//////////////////////////////////////////////////////////////////////
//
// Multiply in-place (*=)
//
//////////////////////////////////////////////////////////////////////

Integer & Integer::operator *= (int ARG) {

  if (ARG < 0) {

    mpz_mul_ui(value, value, (unsigned long int) (- (long int) ARG));
    mpz_neg(value,value);

  } else {

    mpz_mul_ui(value, value, (unsigned long int) ARG);

  }

  return(*this);

}

Integer & Integer::operator *= (mpz_t ARG) {

  mpz_mul(value, value, ARG);

  return(*this);

}

Integer & Integer::operator *= (const Integer & ARG) {

  mpz_mul(value, value, ARG.value);

  return(*this);

}

//////////////////////////////////////////////////////////////////////
//
// Divide in-place (/=)
//
//////////////////////////////////////////////////////////////////////

Integer & Integer::operator /= (int ARG) {

  if (ARG < 0) {

    mpz_tdiv_q_ui(value, value, (unsigned long int) (- (long int) ARG));
    mpz_neg(value,value);

  } else {

    mpz_tdiv_q_ui(value, value, (unsigned long int) ARG);

  }

  return(*this);

}

Integer & Integer::operator /= (const Integer & ARG) {

  mpz_tdiv_q(value, value, ARG.value);

  return(*this);

}

//////////////////////////////////////////////////////////////////////
//
// Bitwise OR in-place (|=)
//
//////////////////////////////////////////////////////////////////////

Integer & Integer::operator |= (int ARG) {

  mpz_t av;

  mpz_init_set_si(av,ARG);

  mpz_ior (value, value, av);

  return(*this);

}

Integer Integer::operator | (int ARG) {

  Integer tmp(value);

  tmp |= ARG;

  return(tmp);

}

//////////////////////////////////////////////////////////////////////
//
// Left shift in-place (<<=)
//
//////////////////////////////////////////////////////////////////////

Integer & Integer::operator <<= (int ARG) {

  assert(ARG >= 0);

  mpz_mul_2exp (value, value, (unsigned long int) ARG);

  return(*this);

}

Integer operator << (const Integer & L, int ARG) {

  assert(ARG >= 0);

  Integer tmp;

  mpz_mul_2exp(tmp.value, L.value, (unsigned long int) ARG);

  return(tmp);

}

Integer operator >> (const Integer & L, int ARG) {

  assert(ARG >= 0);

  Integer tmp;

  mpz_tdiv_q_2exp(tmp.value, L.value, (unsigned long int) ARG);

  return(tmp);

}

//////////////////////////////////////////////////////////////////////
//
// addition (+)
//
//////////////////////////////////////////////////////////////////////

Integer operator + (const Integer & L, const Integer & R) {

  Integer tmp = Integer(L);

  tmp += R;

  return(tmp);

}


Integer Integer::operator + (const Integer & ARG) {

  Integer tmp = Integer(value);

  tmp += ARG;

  return(tmp);

}

Integer Integer::operator + (int ARG) {

  Integer tmp(value);

  tmp += ARG;

  return(tmp);

}

//////////////////////////////////////////////////////////////////////
//
// negation (-)
//
//////////////////////////////////////////////////////////////////////

Integer Integer::operator - () {

  Integer tmp(value);

  mpz_neg(tmp.value,tmp.value);

  return(tmp);

}

//////////////////////////////////////////////////////////////////////
//
// multiplication (*)
//
//////////////////////////////////////////////////////////////////////

Integer operator * (const Integer & L, const Integer & R) {

  Integer tmp = Integer(L);

  tmp *= R;

  return(tmp);

}

Integer Integer::operator * (const Integer & ARG) {

  Integer tmp = Integer(value);

  tmp *= ARG;

  return(tmp);

}

Integer Integer::operator * (int ARG) {

  Integer tmp(value);

  tmp *= ARG;

  return(tmp);

}

void mul (const Integer & M1, const Integer & M2, Integer & R) {

  mpz_mul(R.value, M1.value, M2.value);

}

//////////////////////////////////////////////////////////////////////
//
// Comparison (<, >=)
//
//////////////////////////////////////////////////////////////////////

int operator == (const Integer & R, int L) {

  int sign = 0;

  sign = mpz_cmp_si(R.value,(signed long int) L);

  return(sign == 0);

}

int operator == (int L, const Integer & R) {

  int sign = 0;

  sign = mpz_cmp_si(R.value,(signed long int) L);

  return(sign == 0);

}

int operator != (const Integer & R, int L) {

  int sign = 0;

  sign = mpz_cmp_si(R.value,(signed long int) L);

  return(sign != 0);

}

int operator != (int L, const Integer & R) {

  int sign = 0;

  sign = mpz_cmp_si(R.value,(signed long int) L);

  return(sign != 0);

}

int operator < (int L, const Integer & R) {

  int sign = 0;

  sign = mpz_cmp_si(R.value,(signed long int) L);

  return(sign > 0);

}

int operator <= (const Integer & L, int R) {
  return(! (L > R));
}

int operator > (const Integer & L, int R) {
  return(R < L);
}

int operator >= (const Integer & L, const Integer & R) {

  int sign = 0;

  sign = mpz_cmp(L.value,R.value);

  return(sign >= 0);

}

int operator < (const Integer & L, const Integer & R) {

  int sign = 0;

  sign = mpz_cmp(L.value,R.value);

  return(sign < 0);

}

int operator > (const Integer & L, const Integer & R) {

  int sign = 0;

  sign = mpz_cmp(L.value,R.value);

  return(sign > 0);

}

int operator == (const Integer & L, const Integer & R) {

  int sign = 0;

  sign = mpz_cmp(L.value,R.value);

  return(sign == 0);

}
//////////////////////////////////////////////////////////////////////
//
// subtraction (-)
//
//////////////////////////////////////////////////////////////////////

Integer operator - (const Integer & L, const Integer & R) {

  Integer tmp = Integer(L);

  tmp -= R;

  return(tmp);

}

Integer Integer::operator - (const Integer & ARG) {

  Integer tmp = Integer(value);

  tmp -= ARG;

  return(tmp);

}

//////////////////////////////////////////////////////////////////////
//
// division (/)
//
//////////////////////////////////////////////////////////////////////

Integer Integer::operator / (int ARG) {

  Integer tmp(value);

  tmp /= ARG;

  return(tmp);

}

Integer Integer::operator / (const Integer & ARG) {

  Integer tmp = Integer(value);

  tmp /= ARG;

  return(tmp);

}

void divide(const Integer & N, const Integer & D, Integer & Q, Integer & R) {

  mpz_tdiv_qr(Q.value, R.value, N.value, D.value);

}

//////////////////////////////////////////////////////////////////////
//
// mod (%)
//
//////////////////////////////////////////////////////////////////////

void mod (const Integer & N, const Integer & D, Integer & R) {

  mpz_tdiv_r(R.value, N.value, D.value);

}

Integer Integer::operator % (const Integer & D) {

  Integer tmp;

  mpz_tdiv_r(tmp.value, value, D.value);

  return(tmp);

}

Integer Ipow(long x, long p) {

  Integer tmp;

  assert((x > 0) & (p > 0));

  mpz_ui_pow_ui (tmp.value, (unsigned long int) x, (unsigned long int) p);

  return(tmp);

}

long lg(Integer x) {

  return((long) mpz_sizeinbase(x.value,2));

}

Integer gcd(const Integer & L, const Integer & R) {

  Integer tmp;

  mpz_gcd(tmp.value, L.value, R.value);

  return(tmp);

}

int sign(const Integer & x) {

  return(mpz_sgn(x.value));

}

void setbit(Integer & x, long b) {

  assert(b >= 0);

  mpz_setbit(x.value, (unsigned long int) b);

}

int testbit(const Integer & x, long b) {

  assert(b >= 0);

  mpz_t y;

  mpz_init_set(y,x.value);

  mpz_setbit(y, b);

  int test = mpz_cmp(y, x.value);

  mpz_clear(y);

  return(test == 0);

}

Integer sqr (const Integer & ARG) {

  Integer tmp = Integer(ARG);

  tmp *= tmp;

  return(tmp);

}

